using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.ContentManagement;
using Orchard.ContentManagement.Drivers;
using Orchard.ContentManagement.Handlers;
using Orchard.DisplayManagement;
using Orchard.DisplayManagement.Shapes;
using SchoolPointe.RelatedContent.Models;
using SchoolPointe.RelatedContent.Services;
using SchoolPointe.RelatedContent.ViewModels;
using Orchard.ContentManagement.Records;

namespace SchoolPointe.RelatedContent.Drivers
{
    public class DefaultRelatedContentDriver : IRelatedContentDriver
    {
        private readonly IRelatedContentService _relatedContentService;
        private readonly IContentManager _contentManager;
        private RelatedContentPart _part;
        private string _relationshipIdentifier;
        private string _contentType;

        public DefaultRelatedContentDriver(IRelatedContentService relatedContentService, IContentManager contentManager)
        {
            _relatedContentService = relatedContentService;
            _contentManager = contentManager;
            _contentType = "";
            _relationshipIdentifier = "";
        }

        #region IRelatedContentDriver Members

        public virtual string ContentType
        {
            get { return _contentType; }
        }

        public virtual string RelationshipIdentifier
        {
            get { return _relationshipIdentifier; }
        }

        public virtual ContentItem ContentItem
        {
            get { return _part.ContentItem; }
        }

        public virtual string Prefix { get { return "RelatedContent." + RelationshipIdentifier; } }

        public DriverResult Display(RelatedContentPart part, RelatedContentRelationship relationship, IEnumerable<ContentItemRecord> relatedItems, string displayType, dynamic shapeHelper)
        {
            _part = part;
            _contentType = part.ContentItem.ContentType;
            _relationshipIdentifier = relationship.Identifier;
            return Display(relationship, relatedItems, displayType, shapeHelper);
        }

        protected virtual DriverResult Display(RelatedContentRelationship relationship, IEnumerable<ContentItemRecord> relatedItems, string displayType, dynamic shapeHelper)
        {
            return null;
        }

        public DriverResult Editor(RelatedContentPart part, RelatedContentRelationship relationship, IEnumerable<ContentItemRecord> relatedItems, IUpdateModel updater, dynamic shapeHelper)
        {
            _part = part;
            _contentType = part.ContentItem.ContentType;
            _relationshipIdentifier = relationship.Identifier;
            return Editor(relationship, relatedItems, updater, shapeHelper);
        }

        protected virtual DriverResult Editor(RelatedContentRelationship relationship, IEnumerable<ContentItemRecord> relatedItems, IUpdateModel updater, dynamic shapeHelper)
        {
            var relatedIContent = new List<IContent>();
            foreach (var ri in relatedItems)
            {
                relatedIContent.Add(_contentManager.Get(ri.Id));
            }

            var relationshipVm = new RelatedContentRelationshipViewModel(relatedIContent)
                {
                    Name = relationship.Name,
                    Identifier = relationship.Identifier,
                    IsRequired = relationship.IsRequired,
                    RelationType = relationship.RelationType,
                    EditorTemplate = String.IsNullOrEmpty(relationship.EditorView) ? "RelatedContentRelationshipViewModel" : relationship.EditorView
                };
            if (updater != null)
            {
                updater.TryUpdateModel(relationshipVm, Prefix, null, null);                
                if (!string.IsNullOrEmpty(relationshipVm.RelatedContentIds))
                {
                    var relatedContentIds = relationshipVm.RelatedContentIds.Split(',').Select(id => Convert.ToInt32((string) id)).ToList();
                    SetRelatedItems(relatedContentIds);
                }
            }

            return ContentShape("Parts_RelatedContent_Relationship_Edit", () => shapeHelper.EditorTemplate(
                TemplateName: relationshipVm.EditorTemplate,
                Model: relationshipVm,
                Prefix: Prefix));
        }

        #endregion

        protected void SetRelatedItems(List<int> relatedContentIds)
        {
            _relatedContentService.SetRelatedItems(_part, RelationshipIdentifier, relatedContentIds);
        }

        protected ContentShapeResult ContentShape(string shapeType, Func<dynamic> factory)
        {
            return ContentShapeImplementation(shapeType, ctx => factory());
        }

        protected ContentShapeResult ContentShape(string shapeType, Func<dynamic, dynamic> factory)
        {
            return ContentShapeImplementation(shapeType, ctx => factory(CreateShape(ctx, shapeType)));
        }

        protected ContentShapeResult ContentShapeImplementation(string shapeType, Func<BuildShapeContext, object> shapeBuilder)
        {
            return new ContentShapeResult(shapeType, Prefix, ctx => AddAlternates(shapeBuilder(ctx)));
        }

        protected CombinedResult Combined(params DriverResult[] results)
        {
            return new CombinedResult(results);
        }

        private static object CreateShape(BuildShapeContext context, string shapeType)
        {
            IShapeFactory shapeFactory = context.New;
            return shapeFactory.Create(shapeType);
        }

        private static object AddAlternates(dynamic shape)
        {
            ShapeMetadata metadata = shape.Metadata;
            ContentPart part = shape.ContentPart;
            var id = part != null ? part.ContentItem.Id.ToString() : String.Empty;
            var shapeType = metadata.Type;
            var contentType = part != null ? part.ContentItem.ContentType : String.Empty;

            // [ShapeType]__[Id] e.g. Parts/Common.Metadata-42
            if (!string.IsNullOrEmpty(id))
            {
                metadata.Alternates.Add(shapeType + "__" + id);
            }

            // [ShapeType]__[ContentType] e.g. Parts/Common.Metadata-BlogPost
            if (!string.IsNullOrEmpty(contentType))
            {
                metadata.Alternates.Add(shapeType + "__" + contentType);
            }

            return shape;
        }
    }
}